﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
using System.Text.RegularExpressions;
using System.Reflection;
using System.Data;
using System.Data.SqlClient;
using ZD_DataModel;

namespace ZD_Utility
{
    public class ZD_ReflectionUtility
    {
        #region 私有变量
        static List<string> TypeList = new List<string>() { "Nullable`1", "String", "Int32", "Int64", "Decimal", "Single", "Double", "Guid" };
        #endregion

        //默认构造函数
        public ZD_ReflectionUtility()
        {

        }

        #region 将给定实体类的属性名称与属性值反射成键值对
        /// <summary>
        /// 将给定实体类属性名称与属性值反射成键值对
        /// </summary>
        /// <typeparam name="T">Model实例</typeparam>
        /// <ReferenceTableDIC name="T">外键关联表键值对</ReferenceTableDIC>
        /// <returns></returns>
        public static Dictionary<string, object> MappingModelToKeyAndValue(object Model, ref Dictionary<string, IDataModel> ReferenceTableDIC)
        {
            Dictionary<string, object> List = new Dictionary<string, object>();
            Type type = Model.GetType();
            PropertyInfo[] propertyArr = type.GetProperties();
            //从缓存中获取当前Model的所有属性键值对字典
            object KeyValue = ZD_DataCache.GetCache(type.FullName);
            //从缓存中获取当前Model外键关联表
            object ReferenceTable = ZD_DataCache.GetCache("ReferenceTable_" + type.FullName);
            object[] CustomAttributesArr = null;

            if (KeyValue != null)
            {
                List = (Dictionary<string, object>)KeyValue;
                if (ReferenceTable != null)
                {
                    ReferenceTableDIC = (Dictionary<string, IDataModel>)ReferenceTable;
                }
            }
            else
            {
                foreach (PropertyInfo item in propertyArr)
                {
                    CustomAttributesArr = item.GetCustomAttributes(typeof(ZD_ColumnMappingAttribute), false);
                    if (CustomAttributesArr.Length > 0)
                    {
                        ZD_ColumnMappingAttribute ColumnAttr = (ZD_ColumnMappingAttribute)CustomAttributesArr[0];
                        if (ColumnAttr.ReferenceTableName != null)
                        {
                            ReferenceTableDIC.Add(item.Name.ToLower(), ColumnAttr.ReferenceTableName);
                        }
                        if (!string.IsNullOrEmpty(ColumnAttr.ColumnName))
                        {
                            List.Add(ColumnAttr.ColumnName.ToLower(), item.GetValue(Model, null));
                        }
                        else
                        {
                            List.Add(item.Name.ToLower(), item.GetValue(Model, null));
                        }
                    }
                    else
                    {
                        List.Add(item.Name.ToLower(), item.GetValue(Model, null));
                    }
                }
                ZD_DataCache.InsertCacheHours(type.FullName, List, null, 1);
            }

            return List;
        }

        /// <summary>
        /// 将给定实体类属性名称与属性值反射成键值对
        /// </summary>
        /// <typeparam name="T">Model实例</typeparam>
        /// <returns></returns>
        public static Dictionary<string, object> MappingModelToKeyAndValue(object Model)
        {
            Dictionary<string, object> List = new Dictionary<string, object>();
            //从缓存中获取当前Model的PropertyInfo
            PropertyInfo[] propertyArr = ZD_GetModelCache.GetPropertyInfoByModel(Model);

            //从缓存中获取当前Model的PropertyType
            object[] CustomAttributesArr = null;
            foreach (PropertyInfo item in propertyArr)
            {
                CustomAttributesArr = item.GetCustomAttributes(typeof(ZD_ColumnMappingAttribute), false);
                if (CustomAttributesArr.Length > 0)
                {
                    List.Add(((ZD_ColumnMappingAttribute)CustomAttributesArr[0]).ColumnName.ToLower(), item.GetValue(Model, null));
                }
                else
                {
                    List.Add(item.Name.ToLower(), item.GetValue(Model, null));
                }
            }

            return List;
        }

        /// <summary>
        /// 将给定实体类属性名称与属性值反射成键值对
        /// </summary>
        /// <typeparam name="T">Model实例</typeparam>
        /// <returns></returns>
        public static Dictionary<string, object> MappingModelToKeyAndValue(IDataModel Model, ref Dictionary<string, string> DicModelType)
        {
            Dictionary<string, object> List = new Dictionary<string, object>();
            //从缓存中获取当前Model的PropertyInfo
            PropertyInfo[] propertyArr = ZD_GetModelCache.GetPropertyInfoByModel(Model);
            //从缓存中获取当前Model的PropertyType
            DicModelType = ZD_GetModelCache.GetPropertyTypeByModel(Model);
            object[] CustomAttributesArr = null;

            foreach (PropertyInfo item in propertyArr)
            {
                if (!TypeList.Contains(item.PropertyType.Name))
                    continue;
                CustomAttributesArr = item.GetCustomAttributes(typeof(ZD_ColumnMappingAttribute), false);
                if (CustomAttributesArr.Length > 0)
                {
                    List.Add(((ZD_ColumnMappingAttribute)CustomAttributesArr[0]).ColumnName.ToLower(), item.GetValue(Model, null));
                }
                else
                {
                    List.Add(item.Name.ToLower(), item.GetValue(Model, null));
                }
            }
            return List;
        }
        #endregion

        #region 将给定数据表反射成指定实体类泛型集合
        /// <summary>
        /// 将给定数据表反射成指定实体类泛型集合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static List<T> MappingDataTableToList<T>(DataTable dt)
        {
            if (dt.Rows.Count <= 0) { return default(List<T>); }
            List<T> List = new List<T>();
            T model = Activator.CreateInstance<T>();
            PropertyInfo[] propertyArr = model.GetType().GetProperties();
            foreach (DataRow dr in dt.Rows)
            {
                foreach (PropertyInfo item in propertyArr)
                {
                    if (dt.Columns.Contains(item.Name))
                    {
                        if (dr[item.Name] == DBNull.Value)
                        {
                            item.SetValue(model, null, null);
                        }
                        else
                        {
                            item.SetValue(model, Convert.ChangeType(dr[item.Name], Nullable.GetUnderlyingType(item.PropertyType) ?? item.PropertyType), null);
                        }
                    }
                }
                List.Add(model);
                model = Activator.CreateInstance<T>();
            }
            return List;
        }
        #endregion

        #region 将给定数据行反射成指定实体类
        /// <summary>
        /// 将给定数据行反射成指定实体类
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static T MappingDataRowToModel<T>(DataRow dr)
        {
            if (dr == null) { return default(T); }

            T model = Activator.CreateInstance<T>();
            PropertyInfo[] propertyArr = ZD_GetModelCache.GetPropertyInfoByModel(model);
            foreach (PropertyInfo item in propertyArr)
            {
                if (dr.Table.Columns.Contains(item.Name))
                {
                    if (dr[item.Name] == DBNull.Value)
                    {
                        item.SetValue(model, null, null);
                    }
                    else
                    {
                        item.SetValue(model, Convert.ChangeType(dr[item.Name], Nullable.GetUnderlyingType(item.PropertyType) ?? item.PropertyType), null);
                    }
                }
            }

            return model;
        }
        #endregion

        #region 根据给定SQL语句和实体类生成SqlParameter数组
        /// <summary>
        /// SQL语句和实体类生成SqlParameter数组（必须保证SQL语句中存在的参数在实体类中已存在对应的值）
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sql"></param>
        /// <param name="Model"></param>
        /// <returns></returns>
        public static List<SqlParameter> GetSqlParaBySqlAndModel(string sql, object Model)
        {
            List<SqlParameter> ParaList = new List<SqlParameter>();
            Regex reg = new Regex(@"(?s)\@(?<Column>[^\s\,])");
            MatchCollection macColl = reg.Matches(sql);
            var propertyDictionary = MappingModelToKeyAndValue(Model);

            if (macColl.Count > 0)
            {
                for (int i = 0; i < macColl.Count; i++)
                {
                    if (propertyDictionary.ContainsKey(macColl[i].Groups["Column"].Value.ToLower()))
                    {
                        ParaList.Add(new SqlParameter(macColl[i].Value, propertyDictionary[macColl[i].Groups["Column"].Value]));
                    }
                }
            }
            return ParaList;
        }
        #endregion

        #region 根据给定SQL语句和键值对字典生成SqlParameter数组
        /// <summary>
        /// SQL语句和实体类生成SqlParameter数组（必须保证SQL语句中存在的参数在键值对字典中已存在对应的值）
        /// 此方法主要用于避免多次反射
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sql"></param>
        /// <param name="Model"></param>
        /// <returns></returns>
        public static List<SqlParameter> GetSqlParaBySqlAndDictionary(string sql, Dictionary<String, object> propertyDictionary)
        {
            List<SqlParameter> ParaList = new List<SqlParameter>();
            Regex reg = new Regex(@"(?s)\@(?<Column>[^\s\,)]+)");
            MatchCollection macColl = reg.Matches(sql);

            if (macColl.Count > 0)
            {
                for (int i = 0; i < macColl.Count; i++)
                {
                    if (propertyDictionary.ContainsKey(macColl[i].Groups["Column"].Value.ToLower()))
                    {
                        ParaList.Add(new SqlParameter(macColl[i].Value, propertyDictionary[macColl[i].Groups["Column"].Value]));
                    }
                }
            }
            return ParaList;
        }
        #endregion
    }
}
